package com.phs.main.security.shiro.realms;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.phs.main.security.jwt.JWTToken;
import com.phs.main.security.jwt.JWTUtil;
import com.phs.main.web.po.*;
import com.phs.main.web.service.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class LoginRealm extends AuthorizingRealm {
	@Lazy
	@Autowired
	ISysUsersService usersService;
	@Lazy
	@Autowired
	ISysUsersRolesService usersRolesService;
	@Lazy
	@Autowired
	ISysRolesService rolesService;
	@Lazy
	@Autowired
	ISysRolesPermissionsService rolesPermissionsService;
	@Lazy
	@Autowired
	ISysPermissionsService permissionsService;

	/**
	 * 使用JWT替代原生Token
	 *
	 * @param token token
	 * @return boolean
	 */
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof JWTToken;
	}

	/**
	 * 授权，用户的角色信息集合,只有当需要检测用户权限的时候才会调用此方法
	 * <p>
	 * Subject 主体
	 * Principal 身份信息
	 * Credential 凭证信息
	 *
	 * @param principalCollection 主体信息
	 * @return 权限授权信息
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		//	获取当前用户信息
		Subject subject = SecurityUtils.getSubject();
		SysUsers account = (SysUsers) subject.getPrincipal();

		//	设置角色集合
		Set<String> roles = new HashSet<>();
		//	设置权限集合
		Set<String> perms = new HashSet<>();

		//	通过UserID 获得与其关联的RolesID
		QueryWrapper<SysUsersRoles> u_r_wrapper = new QueryWrapper<>();
		u_r_wrapper.eq("user_ID", account.getUserId());

		//	返回为空时不执行lambda 语句
		List<SysUsersRoles> sysUsersRoles = usersRolesService.list(u_r_wrapper);
		sysUsersRoles.forEach(userRole -> {
			//	通过RoleID 获得Role
			QueryWrapper<SysRoles> r_wrapper = new QueryWrapper<>();
			r_wrapper.eq("role_ID", userRole.getRoleId());

			//	获得role 列表，加入roles 集合
			SysRoles rolesServiceOne = rolesService.getOne(r_wrapper);
			String roleName = rolesServiceOne.getRoleName();
			roles.add(roleName);

			// 根据roleID 获得关联的perm ID
			QueryWrapper<SysRolesPermissions> r_p_wrapper = new QueryWrapper<>();
			r_p_wrapper.eq("role_ID", userRole.getRoleId());

			//	设置权限
			List<SysRolesPermissions> rolesPermissions = rolesPermissionsService.list(r_p_wrapper);
			rolesPermissions.forEach(rolePerm -> {
				//	通过permID 获得 permName
				QueryWrapper<SysPermissions> p_wrapper = new QueryWrapper<>();
				p_wrapper.eq("permission_ID", rolePerm.getPermissionId());
				// 获得perm
				SysPermissions permissions = permissionsService.getOne(p_wrapper);
				String permissionName = permissions.getPermissionName();

				perms.add(roleName + ":" + permissionName);
			});
		});

		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		if (!roles.isEmpty()) {
			info.addRoles(roles);
		}
		if (!perms.isEmpty()) {
			info.addStringPermissions(perms);
		}
		return info;
	}

	/**
	 * 登录时认证，角色的权限信息集合
	 *
	 * @param authenticationToken token
	 * @return 认证信息
	 * @throws AuthenticationException 报错，一般会有两种情况：无用户、无权限
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		// 解密获得username，用于和数据库进行对比
		String token = (String) authenticationToken.getCredentials();
		if (JWTUtil.isExpire(token)) {
			throw new AuthenticationException(" token过期，请重新登入！");
		}


		String username = JWTUtil.getUsername(token);

		if (username == null) {
			throw new AuthenticationException(" token错误，请重新登入！");
		}

		QueryWrapper<SysUsers> wrapper = new QueryWrapper<>();
		wrapper.eq("tel", username);

		//用户名验证
		SysUsers account = usersService.getOne(wrapper);

		if (!JWTUtil.verify(token, username, account.getPassword())) {
			throw new CredentialsException("密码错误!");
		}
		if (account.getLocked() == 1) {
			throw new LockedAccountException("账号已被锁定!");
		}


		//	密码验证
		return new SimpleAuthenticationInfo(account, token, getName());

	}
}
